/* symbol table implementation */

#include <iomanip>
#include <sstream>
#include "symboltable.h"
#include "ast.h"

using std::map;
using std::string;
using std::setw;
using std::setbase;
using std::setfill;
using std::stringstream;
using std::endl;


SymbolTable::~SymbolTable()
{
	// Clean up only the labels
	map<string,Label*>::iterator i;
	for(i = jumps.begin(); i != jumps.end(); ++i) {
		delete i->second;
	}
}

/*
 * Sets a parent symbol table for scope chaining
 */
void SymbolTable::SetParent(SymbolTable* parent)
{
	this->parent = parent;
}


/*
 * Adds a base address to the targets of all labels defined in the table
 */
void SymbolTable::AddBaseAddress(unsigned int base)
{
	map<string,Label*>::iterator i;
	for(i = jumps.begin(); i != jumps.end(); ++i) {
		Label* lbl = i->second;
		lbl->SetTarget(lbl->GetTarget() + base);
	}
}

/*
 * Maps a symbol name to an AST node.
 * Node should be a definition of a const or a command.
 * TODO: make type requirement explicit. May require refactoring of ConstDef/CommandDef
 */
bool SymbolTable::Define(const string& name, Node* node)
{
	bool retval = false;
	if(table.find(name) != table.end())
		retval = true;
	table[name] = node;
	return retval;
}

/*
 * Defines a label. Returns true if the label's name was already mapped.
 */
bool SymbolTable::DefineLabel(Label* label)
{
	return DefineLabel(label->GetName(), label);
}

/*
 * Defines a label mapped to the specified name.
 * Returns true if the name was already mapped.
 */
bool SymbolTable::DefineLabel(const string& name, Label* label)
{
	bool retval = false;
	if(jumps.find(name) != jumps.end())
		retval = true;
	jumps[name] = label;
	return retval;
}

/*
 * Looks up the given symbol in this table and all parent tables,
 * returning the node associated with the first mapping found,
 * or NULL if the symbol is not defined.
 */
Node* SymbolTable::Lookup(const string& name) const
{
	map<string,Node*>::const_iterator f = table.find(name);
	if(f == table.end()) {
		if(parent) return parent->Lookup(name);
		else return NULL;
	}
	return f->second;
}

/*
 * Looks up a label name in this and all parent tables, returning
 * the label associated with the first mapping found, or NULL if
 * no label is defined with the given name.
 */
Label* SymbolTable::LookupLabel(const string& name) const
{
	map<string,Label*>::const_iterator f = jumps.find(name);
	if(f == jumps.end()) {
		if(parent) return parent->LookupLabel(name);
		else return NULL;
	}
	return f->second;
}

/*
 * Returns a string representation of the symbol table.
 */
string SymbolTable::ToString() const
{
	stringstream ss;
	ss << "NAME                     TYPE      VALUE" << endl;

	map<string,Node*>::const_iterator i;
	for(i = table.begin(); i != table.end(); ++i) {
		// Output name
		ss << setfill(' ') << setw(25) << std::left << i->first;

		// Output type
		nodetype t = i->second->GetType();
		//if(t == conststmt)
		//	t = ((ConstDef*)i->second)->GetValue()->GetType();

		string st;
		switch(t) {
			case commandstmt: st = "command"; break;
			case conststmt: st = "const"; break;
			case intexpr: st = "int"; break;
			case stringexpr: st = "string"; break;
			default: st = "<invalid>";
		}
		ss << setw(10) << std::left << st;

		// Trim newlines and tabs out of the value
		string val = i->second->ToString("");
		if(t == conststmt)
			val = ((ConstDef*)i->second)->GetValue()->ToString("");

		string::size_type vpos = val.find("\n");
		while(vpos != string::npos) {
			val.erase(vpos,1);
			vpos = val.find("\n");
		}

		ss << val << "\n";
	}

	return ss.str();
}

/*
 * Returns a string representation of the jumps table
 */
string SymbolTable::JumpsTable() const
{
	stringstream ss;
	ss << "LABEL                    ADDRESS" << endl;
	map<string,Label*>::const_iterator i;
	for(i = jumps.begin(); i != jumps.end(); ++i) {
		ss << setw(25) << std::left << i->first;
		ss << setbase(16) << i->second->GetTarget() << endl;
	}
	return ss.str();
}

/*
 * Returns a const reference to the label table
 */
const map<string, Label*>& SymbolTable::GetJumpTable() const
{
	return jumps;
}

